package com.doudou.collection.infrastructure.core.config;

import com.alibaba.fastjson.JSON;
import com.doudou.collection.infrastructure.common.web.ResultVO;
import com.doudou.collection.infrastructure.common.web.StatusCode;
import com.doudou.collection.infrastructure.core.filter.JwtAuthenticationTokenFilter;
import com.doudou.collection.infrastructure.core.security.handle.LogoutSuccessHandlerImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @ClassName SecurityConfig
 * @Description Security框架配置类
 * @Author blue sky
 * @Date 2023/6/1 23:17
 * @Version 1.0
 */
@Slf4j
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthorizationFilter;

    @Autowired
    private LogoutSuccessHandlerImpl logoutSuccessHandler;

    //配置密码加密的方式
    @Bean
    public PasswordEncoder passwordEncoder() {
        //返回此加密的编码器之后, 用户输入的密码
        //会通过此编码器加密之后再和数据库里面的密码进行比较
        return new BCryptPasswordEncoder();
    }

    @Bean
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 配置Security框架不使用Session
        // SessionCreationPolicy.NEVER：从不主动创建Session，但是，Session存在的话，会自动使用
        // SessionCreationPolicy.STATELESS：无状态，无论是否存在Session，都不使用
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 将自定义的解析JWT的过滤器添加到Security框架的过滤器链中
        // 必须添加在检查SecurityContext的Authentication之前，具体位置并不严格要求
        http.addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class);

        // 添加Logout filter
        http.logout().logoutUrl("/dev-api/logout").logoutSuccessHandler(logoutSuccessHandler);

        // 处理“无认证信息却访问需要认证的资源时”的响应
        http.exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {
            @Override
            public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
                log.warn("{}", e.getMessage());
                response.setContentType("application/json; charset=utf-8");
                String message = "操作失败，您当前未登录！";
                ResultVO resultVO = ResultVO.error(StatusCode.ERROR_UNAUTHORIZED, message);
                PrintWriter writer = response.getWriter();
                writer.println(JSON.toJSONString(resultVO));
                writer.close();
            }
        });

        // 允许跨域访问
        http.cors();

        //设置黑名单(需要登录才可访问的资源)
        String[] urls = {};
        http.authorizeHttpRequests()  //对请求进行授权
                .mvcMatchers(urls)    //匹配某些路径
                .authenticated()      //需要通过登录认证
                .anyRequest()         //其它任意请求
                .permitAll();         //直接放行, 即不需要登录也可以访问

        //关闭跨域攻击防御策略
        http.csrf().disable();
    }
}
